#ifndef _C_GL_FRAMEBUFFER_H_
#define _C_GL_FRAMEBUFFER_H_

#include "../glew/glew.h"

#include "../interface/I_api_Framebuffer.h"

class C_gl_Framebuffer: public I_api_Framebuffer
{
protected: 
	friend class C_gl_Device;

	int		m_Id;
	int		m_RTV[E_API_SIMULTANEOUS_RENDER_TARGET_COUNT];
	int		m_DepthId;
	int		m_StencilId;

	C_gl_Framebuffer(int nRenderTargetView, I_api_RenderTargetView ** ppRenderTargetView, I_api_DepthStencilView *pDepthStencilView) 
		: I_api_Framebuffer(nRenderTargetView, ppRenderTargetView, pDepthStencilView)
	{
		//@ to access glut fbo pass 0, NULL, NULL
		//@ this will still be a valid fbo
		m_Id = 0;
		m_DepthId = 0;
		m_StencilId = 0;

		//@ check that passed rtv and dsv are valid
		//@ for example, rtv != NULL dsv != NULL - color & depth
		//@ for example, rtv == NULL dsv != NULL - shadow map
		//@ for example, rtv != NULL dsv == NULL - post processing
		if (nRenderTargetView <= 0 && ppRenderTargetView == NULL && pDepthStencilView == NULL) return;

		glGenFramebuffersEXT(1, (GLuint *) &m_Id );
		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_Id );

		if (ppRenderTargetView != NULL)
		{
			for (int iRTV =0; iRTV < nRenderTargetView; iRTV++ )
			{		
				S_api_RenderTargetView_Desc rtvDesc;
				ppRenderTargetView[iRTV]->GetDesc(&rtvDesc);

				I_api_Resource * pResource;
				ppRenderTargetView[iRTV]->GetResource( &pResource );

				I_api_Texture_2D * pTexture = dynamic_cast<I_api_Texture_2D *>(pResource);
				
				//@ in ogl rtv and resource have the same format
				//@ but in general this is not the case 
				//@ for example in d3d10 you can view a resource through different rtv
				int	texId = *((int *)pTexture->Get());
				int rtvId = *((int *)ppRenderTargetView[iRTV]->Get());

				//glBindTexture(GL_TEXTURE_2D, texId );
				glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT+iRTV, GL_TEXTURE_2D, rtvId, rtvDesc.Texture2D.MipSlice );

				m_RTV[iRTV] = GL_COLOR_ATTACHMENT0_EXT + iRTV;
			}
		}
		else
		{
			//@ disable color buffer if you don't attach any color buffer image,
			//@ Otherwise, glCheckFramebufferStatusEXT will not be complete.
			glDrawBuffer(GL_NONE);
			glReadBuffer(GL_NONE);
		}

		if (pDepthStencilView)
		{
			S_api_DepthStencilView_Desc dsvDesc;
			pDepthStencilView->GetDesc(&dsvDesc);
			
			I_api_Resource * pResource;
			pDepthStencilView->GetResource( &pResource );

			I_api_Texture_2D	*	pTexture = dynamic_cast<I_api_Texture_2D *>(pResource);

			S_api_Texture2D_Desc tex2dDesc;
			pTexture->GetDesc(&tex2dDesc);

			//@ in ogl dsv and resource have the same format
			//@ but in general this is not the case 
			//@ for example in d3d10 you can view a resource through different dsv
			int	texId = *((int *)pTexture->Get());
			int dsvId = *((int *)pDepthStencilView->Get());

			glGenRenderbuffersEXT(1, (GLuint *) &m_DepthId);
			glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_DepthId);
			glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, tex2dDesc.Width, tex2dDesc.Height);
			glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);

			//@ in case dsv has stencil create stencil renderbuffer
			if (dsvDesc.Format == E_api_Format_d24_unorm_s8_uint || dsvDesc.Format == E_api_Format_d32_float_s8x24_uint )	
			{
				//glGenRenderbuffersEXT(1, (GLuint *) &m_StencilId);
				//glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_StencilId);
				//glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, tex2dDesc.Width, tex2dDesc.Height);
				//glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);

				////glBindTexture(GL_TEXTURE_2D, texId );
				//glFramebufferTexture2DEXT ( GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, dsvId, dsvDesc.Texture2D.MipSlice );
			}
			else
			{
				//glBindTexture(GL_TEXTURE_2D, texId );
				glFramebufferTexture2DEXT ( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, dsvId, dsvDesc.Texture2D.MipSlice );
			}
		}

		//glBindTexture(GL_TEXTURE_2D, 0 );
		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

		Check();
	}

	virtual	void	OMGetRenderTargets(int nRenderTargetView, I_api_RenderTargetView ** ppRenderTargetView, I_api_DepthStencilView ** pDepthStencilView)
	{
		I_api_Framebuffer::OMGetRenderTargets(nRenderTargetView, ppRenderTargetView, pDepthStencilView);
	}

virtual	void	OMSetRenderTargets()
	{
		if (m_Id)
		{
			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_Id);

			if (m_pRenderTargetViewList.size()) 
				glDrawBuffers(m_pRenderTargetViewList.size(), (GLenum *) m_RTV);
			else 
			{
				glDrawBuffer(GL_NONE);
				glReadBuffer(GL_NONE);
			}
		}
		else
		{
			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
			glDrawBuffer(GL_BACK);
			glReadBuffer(GL_BACK);			
		}

		Check();
	}

	void	Check()
	{
		GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);		
		if (status != GL_FRAMEBUFFER_COMPLETE_EXT) 
		{
			fprintf(stderr, "Error creating framebuffer : ");

			switch (status)
			{
			case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: fprintf(stderr, "Incomplete attachement! \n"); break;
			case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: fprintf(stderr, "Incomplete missing attachement! \n"); break;
			case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: fprintf(stderr, "Incomplete draw buffer! \n"); break;
			case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: fprintf(stderr, "Incomplete read buffer! \n"); break;
			case GL_FRAMEBUFFER_UNSUPPORTED_EXT: fprintf(stderr, "Framebuffer not supported! \n"); break;
			default: fprintf(stderr, "Unknown gl errod! \n"); break;
			}
		}
	}

public :


};

#endif